/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_orm.htm
 *
 * Zhiqim ORM is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.orm.executor;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.zhiqim.kernel.model.maps.HashMapSO;
import org.zhiqim.kernel.model.maps.LinkedMapSO;
import org.zhiqim.kernel.model.maps.MapSO;
import org.zhiqim.kernel.model.maps.MapSS;
import org.zhiqim.kernel.model.maps.TreeMapSO;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.util.Closes;
import org.zhiqim.kernel.util.Sqls;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.orm.ORMException;
import org.zhiqim.orm.ORMServer;
import org.zhiqim.orm.datasource.ZResultSet;
import org.zhiqim.orm.datasource.ZStatement;
import org.zhiqim.orm.dbo.CallParam;
import org.zhiqim.orm.dbo.CallResult;
import org.zhiqim.orm.dbo.Dbo;
import org.zhiqim.orm.exception.TableNotExistException;

public class CallExecutor
{
    public final Log log = LogFactory.getLog(QueryExecutor.class);
    public final Log cSqlLog = LogFactory.getLog("call.sql.log");
    
    /**
     * 执行executeQuery处理，基本类型不需要判断是否有通配符##,sql语句用?代替
     * 
     * @param server        ORM服务
     * @param conn          数据库连接
     * @param sql           ZSQL语句
     * @param paramList     参数表
     * @param resultList    结果集表
     * @param replaceMap    可替换表
     * @throws ORMException 映射异常
     */
    public void execute(ORMServer server, Connection conn, String sql, List<CallParam> paramList, List<CallResult> resultList, MapSS replaceMap) throws ORMException
    {
        CallableStatement cstmt = null;
        ResultSet rst = null;

        try
        {
            //1.替换可替换符$$
            sql = Sqls.formatReplaceMap(sql, replaceMap);
            cstmt = conn.prepareCall(sql);
          
            //设置IN/INOUT参数
            for (int i=0;i<paramList.size();i++)
            {
                CallParam param = paramList.get(i);
                if (param.isInWrite())
                    ZStatement.setParameter(server, cstmt, i+1, param.value());
                else
                    cstmt.registerOutParameter(i+1, param.typeSQL());
            }

            if (server.isQuerySqlLog())
            {//查询可以打印SQL
                cSqlLog.info(sql);
            }

            //执行
            cstmt.execute();
            
            //读取OUT/INOUT参数
            for (int i=0;i<paramList.size();i++)
            {
                CallParam param = paramList.get(i);
                if (param.isOutRead())
                    param.value(cstmt, i+1);
            }
            
            //读取结果集
            if (Validates.isNotEmpty(resultList))
            {
                int index = 0;
                
                do
                {
                    rst = cstmt.getResultSet();
                    if (rst == null)
                        break;
                    
                    CallResult callResult = resultList.get(index);
                    Class<?> resultClass = callResult.getResultClass();
                    
                    if (server.isResultBase(resultClass))
                        callResult.setResultList(ZResultSet.parseToBase(rst, resultClass));
                    else if (resultClass == Map.class || resultClass == MapSO.class || resultClass == HashMapSO.class || resultClass == LinkedMapSO.class || resultClass == TreeMapSO.class)
                        callResult.setResultList(ZResultSet.parseToMap(rst, resultClass));
                    else if (server.isDbo(resultClass))
                    {
                        Dbo resultDbo = server.getDbo(resultClass);
                        callResult.setResultList(ZResultSet.parseToDbo(rst, resultDbo));
                    }
                    else
                    {//解析不了
                        throw new ORMException("调用[函数/存储过程]时不支持的结果集类["+resultClass+"]");
                    }
                    
                    Closes.closeIgnoreException(rst);
                    rst = null;
                    index++;
                }
                while (cstmt.getMoreResults() && resultList.size() > index);
            }
        }
        catch (SQLException e) 
        {
            if (server.getPolicy().isTableNotExist(e))
                throw new TableNotExistException("[%s][%s]", e, e.getMessage(), sql);
            else
                throw new ORMException("[%s][%s]", e, e.getMessage(), sql);
        }
        catch (Throwable e)
        {
            throw new ORMException("[%s][%s]", e, e.getMessage(), sql);
        }
        finally
        {
            Closes.closeIgnoreException(rst, cstmt);
        }
    }
}
